﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace PhantomMvvmToolkit.Converter
{

    

    /// <summary>
    /// This will convert a boolean value, numeric value, or a string value into a SolidColorBrush.
    /// </summary> 
    /// <remarks>
    /// 1. String Hex Value (#AABBCC) or (#AABBCCDD)
    /// 2. String RBG value (RGB111,222,333) or (RGBA111,222,333,444)
    /// 3. Color struct
    /// 4. Int, Float, Double that range from 0 to 1 (it will convert it to a gray scale)
    /// 5. Boolean (to black and white)
    /// 
    /// For strings it will look in the parameters for one of the follow values "HEX3", "HEX4", "RGB", or "RGBA".  That will define what format it will build the 
    /// outbound string to, default will be HEX4
    /// </remarks>
    /// <exception cref="InvalidOperationException"></exception>
    public class ToSolidColorBrush : IValueConverter
    {
        private static List<string> ValidStringFormats = new List<string> { "HEX3", "HEX4", "RGB", "RGBA" };

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            SolidColorBrush result;

            if (value is Color)
            {
                var casted = (Color)value;
                return BuildSolidColorBrush(casted.A, casted.R, casted.B, casted.G);
            }
            if (value is bool)
            {
                if ((bool)value)
                    result = BuildSolidColorBrush(255, 255, 255, 255);
                else
                    result = BuildSolidColorBrush(255, 0, 0, 0);
            }
            else if (value is int)
            {
                var casted = (int) value;

                result = casted <= 0 ? BuildSolidColorBrush(255, 0, 0, 0) : BuildSolidColorBrush(255, 255, 255, 255);
            }
            else if (value is float)
            {
                var casted = (float)value;
                var intCode = (int)(casted*255);
                var bitCode = byte.Parse(Math.Max(0, Math.Min(255, intCode)).ToString());

                result = BuildSolidColorBrush(255, bitCode, bitCode, bitCode);
            }
            else if (value is double)
            {
                var casted = (double)value;
                var intCode = (int)(casted * 255);
                var bitCode = byte.Parse(Math.Max(0, Math.Min(255, intCode)).ToString());

                result = BuildSolidColorBrush(255, bitCode, bitCode, bitCode);
            }
            else if (value is string)
            {
                var casted = (string)value;
                casted = casted.ToUpper();

                if (string.IsNullOrEmpty((string)value))
                {
                    return BuildSolidColorBrush(255, 0, 0, 0);
                }
                else if (casted.StartsWith("#") && casted.Length == 7 || casted.Length == 9)
                {
                    byte[] hexValues;
                    try
                    {
                        hexValues = Enumerable.Range(0, casted.Length - 1)
                             .Where(x => x % 2 == 0)
                             .Select(x => System.Convert.ToByte(casted.Substring(x + 1, 2), 16))
                             .ToArray();
                    }
                    catch (Exception)
                    {
                        throw new InvalidOperationException(string.Format("Failed to convert string value {0} from hex to byte array.", casted));
                    }

                    byte a = 255;
                    byte r;
                    byte g;
                    byte b;

                    if (hexValues.Length == 3)
                    {
                        r = hexValues[0];
                        g = hexValues[1];
                        b = hexValues[2];
                    }
                    else
                    {
                        a = hexValues[0];
                        r = hexValues[1];
                        g = hexValues[2];
                        b = hexValues[3];
                    }
 
                    return BuildSolidColorBrush(a, r, b, g);
                }
                else if (casted.StartsWith("RGB"))
                {
                    var rbg = casted.Substring(3).Split(',');

                    byte r;
                    byte g;
                    byte b;

                    try
                    {
                        r = byte.Parse(rbg[0]);
                        g = byte.Parse(rbg[1]);
                        b = byte.Parse(rbg[2]);
                    }
                    catch (Exception)
                    {
                        throw new InvalidOperationException(string.Format("Failed to convert string value {0} from RBG to byte array.", casted));
                    }

                    return BuildSolidColorBrush(255, r, b, g); 
                }
                else if (casted.StartsWith("RGBA"))
                {
                    var rbg = casted.Substring(4).Split(',');

                    byte a;
                    byte r;
                    byte g;
                    byte b;

                    try
                    {
                        r = byte.Parse(rbg[0]);
                        g = byte.Parse(rbg[1]);
                        b = byte.Parse(rbg[2]);
                        a = byte.Parse(rbg[3]);
                    }
                    catch (Exception)
                    {
                        throw new InvalidOperationException(string.Format("Failed to convert string value {0} from RBGA to byte array.", casted));
                    }

                    return BuildSolidColorBrush(a, r, b, g); 
                }
                else
                {
                    throw new InvalidOperationException(string.Format("Type is string but it did not convert to a valid hex, rgb, or rgba value.  It's value was {0}", value));
                }
            }
            else if (value == null)
            {
                result = BuildSolidColorBrush(255, 0, 0, 0);
            }
            else
            {
                throw new InvalidOperationException(string.Format("Type {0} is not a valid conversion type.", value.GetType().Name));
            }

            return result;
        }

        static ToSolidColorBrush()
        {
            BuiltBrushes = new Dictionary<string, SolidColorBrush>();
        }

        private static Dictionary<string, SolidColorBrush> BuiltBrushes { get; set; }

        private static SolidColorBrush BuildSolidColorBrush(byte a, byte r, byte b, byte g)
        {
            var key = string.Format("{0}{1}{2}{3}", a, b, g, r);

            if (!BuiltBrushes.ContainsKey(key))
            {
                BuiltBrushes.Add(key, new SolidColorBrush(new Color() { A = a, B = b, G = g, R = r }));
            }

            return BuiltBrushes[key];
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            var current = ((SolidColorBrush)value).Color;

            if (targetType == typeof(Color))
            {
                return current;
            }

            if (targetType == typeof(bool) || targetType == typeof(bool?))
            {
                return current != Colors.Black;
            }

            if (targetType == typeof(int) || targetType == typeof(int?))
            {
                return current == Colors.Black ? 0 : 1;
            }

            if (targetType == typeof(float) || targetType == typeof(double) ||
                targetType == typeof(float?) || targetType == typeof(double?))
            {
                // takes the avg of the rgb (thus converting it to grey scale if it is not already)
                return (new List<int>
                {
                    System.Convert.ToInt32(current.R),
                    System.Convert.ToInt32(current.G),
                    System.Convert.ToInt32(current.B)
                }).Average() / 255;
            }

            if (targetType == typeof(string))
            {
                var format = "HEX4";

                if (parameter != null && ValidStringFormats.Contains(parameter.ToString()))
                {
                    format = parameter.ToString();
                }

                switch (format)
                {
                    case "HEX3":
                        return string.Format("#{0}{1}{2}", 
                            System.Convert.ToString(current.R, 16),
                            System.Convert.ToString(current.G, 16),
                            System.Convert.ToString(current.B, 16)).ToUpper();
                    case "HEX4":
                        return string.Format("#{0}{1}{2}{3}", 
                            System.Convert.ToString(current.A, 16),
                            System.Convert.ToString(current.R, 16),
                            System.Convert.ToString(current.G, 16),
                            System.Convert.ToString(current.B, 16)).ToUpper(); 
                    case "RGB":
                        return string.Format("RGB{0},{1},{2}", 
                            current.R, current.G, current.B);
                    case "RGBA":
                        return string.Format("RGBA{0},{1},{2},{3}",
                            current.R, current.G, current.B, current.A);
                }
            }

            throw new InvalidOperationException(string.Format("Type {0} is not a valid conversion type.", targetType.Name));
        }
    }
}
